#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>

#define SERVER_PORT 9003
void sys_err(const char *str)
{
  perror(str);
  exit(1);
}

struct s_info
{
  struct sockaddr_in cliaddr;
  int connfd;   // 文件描述符
  pthread_t id; // 子线程id
};

struct s_info ts[256] = {0}; // 为什么要定义一个数组

void *do_work(void *arg)
{
  struct s_info *ts = (struct s_info *)arg;
  char buf[BUFSIZ];
  char client_IP[1024] = {0};
  while (1)
  {
    //       *  >0 实际督导字节数
    //       *   0 读到末尾(对端已经关闭)
    //       *  -1： 读取失败  应该进一步判断errno的值
    //           errno = EAGAIN or EWOULDBLOCK: 设置了非阻塞方式读
    //           errno = EINTR  慢速系统调用被 中断 , 解决方法重新 重新运行读取 goto
    //           errno =" 其他情况" 异常
    int n = read(ts->connfd, buf, sizeof(buf));
    if (n == 0)
    { // 客户端关闭,已经读到套接子的末尾
      printf("the client %d closed...\n", ts->connfd);
      break;
    }
    else if (n == -1)
    {
      perror("error");
      pthread_exit(NULL); // 退出自己
    }
    else
    {
      printf("receiver from client  ip:%s port:%d  tid:%ld  fd: %d\n", inet_ntop(AF_INET, &(*ts).cliaddr.sin_addr, client_IP, sizeof(client_IP)),
             ntohs((*ts).cliaddr.sin_port),
             ts->id,
             ts->connfd);
      for (int i = 0; i < n; i++)
      {
        buf[i] = toupper(buf[i]);
      }
      write(STDOUT_FILENO, buf, n);
      write(ts->connfd, buf, n);
    }
  }
  close(ts->connfd);
  return 0;
}


//发送给某一个用户

void *send_mess_one(void *arg)
{
#if 0
    int ret;
    char buf[50];
    int newfd;
    char *p;
    pthread_detach(pthread_self());
    while (1) {
        bzero(buf, 50);
        printf("<newfd:string> ");
        fgets(buf, 50, stdin); //阻塞
        p = strtok(buf, ":");
        newfd = atoi(p);
        p = strtok(NULL, ":");
        ret = send(newfd, p, strlen(p), 0);
        printf("send %d bytes\n", ret);
        if (strncmp(p, "quit\n", 5) == 0) break;
    }
    return NULL;
#endif 
}

 //广播给所有用户
void *send_mess_all(void *arg)
{
  int ret;
  char buf[50];

  pthread_detach(pthread_self());
  while (1)
  {
    bzero(buf, 50);
    fgets(buf, 50, stdin); // 阻塞
    // 遍历链表上每个节点来发送给客户端
    // lock
    //  link_node *temp; //临时存放遍历链表节点的指针
    //  temp = head->next;
    //  if (temp == NULL)
    //      printf("all client offline\n");
    //  else {
    //      while (temp != NULL) {
    //          printf("send to newfd=%d\n", temp->newfd);
    //          ret = send(temp->newfd, buf, strlen(buf), 0);
    //          printf("send %d bytes\n", ret);
    //          temp = temp->next;
    //      }
    //  }
    // unlock
  //  printf("-%d\n", sizeof(ts) / sizeof(struct s_info));
  /**
   * 广播给所有客户端
   */
    for(int i=0;i<sizeof(ts) / sizeof(struct s_info);i++){
         struct s_info ts1= ts[i];
         if(ts1.connfd != -1) {
               ret = send(ts1.connfd, buf, strlen(buf), 0);
              //  printf("send %d bytes\n", ret);
              //  printf("newfd: %d\n",ts1.connfd);
         }
    }
  }
  return NULL;
}

int main(int argc, char *argv[])
{

  int lfd = 0, cfd = 0;
  int ret, i;
  pid_t pid;
  struct sockaddr_in serv_addr, clit_addr;
  socklen_t clit_addr_len;
  char client_IP[1024] = {0};

  pthread_t tid;

  //  memset(&serv_addr,0,sizeof(serv_addr));
  bzero(&serv_addr, sizeof(serv_addr)); // 和memset功能一致的

  serv_addr.sin_family = AF_INET;                // IP V4
  serv_addr.sin_port = htons(SERVER_PORT);       // 端口
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY 表示根据本机自动分配IP 地址

  lfd = socket(AF_INET, SOCK_STREAM, 0); // IP  流式套接子   0表示根据第2个参数推断tcp
  if (lfd == -1)
  {
    sys_err("socket error");
  }

  int on = 1;
  setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); // 设置网络属性重用地址

  bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); // 这里表示IP 地址成功绑定 socket

  listen(lfd, 128); // 这里表示设置三次握手的最大并发数
  clit_addr_len = sizeof(clit_addr);
  // 上面就是三次握手

  // if have thread is exit,  use
  for (i = 0; i < 256; i++)
  {
    ts[i].connfd = -1;
  }

  // 创建键盘输入线程
  pthread_create(&tid, NULL, send_mess_all, NULL);

  while (1)
  {
    for (i = 0; i < 256; i++)
    {
      if (ts[i].connfd == -1)
      {
        break;
      }
    }
    // 数组满了 , 不收了
    if (i == 256)
    {
      break;
    }

    memset(client_IP, 0, sizeof(client_IP));
    ts[i].connfd = accept(lfd, (struct sockaddr *)&ts[i].cliaddr, &clit_addr_len); // 这里阻塞，在这里接收连接上的客户端
    printf("accept  suc...\n");
    // clit_addr 客户端数据保存在这里
    if (ts[i].connfd == -1)
    {
      sys_err("accpet error");
    }
    //  ts[i].cliaddr=clit_addr;

    //  pthread_create(&tid,NULL,do_work,(void*)&ts[i]);
    pthread_create(&ts[i].id, NULL, do_work, (void *)&ts[i]);
    // ts[i].id = tid;

    // pthread_detach(tid);
    pthread_detach(ts[i].id);
    //  i++;
  }
  close(lfd);
  // 数组满了，退出主线程， 子线程继续跑
  printf("main exit\n");
  pthread_exit(NULL);

  return 0;
}